/**
 * @file
 * @author dousha
 * @date 2020
 */

#include "stm32f4xx.h"

#include "clock.h"
#include "../interrupt/interrupt.h"
#include "../task/task.h"

static volatile uint32_t decade;
static volatile uint32_t ticks;

/**
 * 初始化系统时钟。
 *
 * @kernelOnly
 */
void clock_init() {
	SysTick_Config(SYSTEM_FREQUENCY / SYSTICK_FREQUENCY);
}

/**
 * 获取当前系统时间。
 * 
 * @attention 当前系统时间可能会发生上溢。如果必要，应先使用 clock_decade_get 获取计数周期，再获取计数值。
 *
 * @return 当前系统时间，单位为「刻」。
 */
uint32_t clock_get() {
	return ticks;
}

/**
 * 获取当前系统周期。
 * 
 * 当系统时间发生一次溢出时，系统周期会加一。
 * 
 * @return 当前系统周期
 */
uint32_t clock_decade_get() {
	return decade;
}

/**
 * 计算给定时间与当前时间的差。
 * 
 * 当传入的时间是过去的时间时，返回的值为正；否则为负。
 * 
 * @param decade 周期
 * @param time 时间
 * @return 给定时间戳和现在时间的差距
 */
int32_t clock_ticks_from_now(uint32_t decade, uint32_t time) {
	uint64_t currenFullTime = clock_decade_get(), givenFullTime = decade;
	currenFullTime <<= 32u;
	currenFullTime += clock_get();
	givenFullTime <<= 32u;
	givenFullTime += time;
	if (currenFullTime > givenFullTime) {
		return (int32_t) (currenFullTime - givenFullTime);
	} else {
		return (int32_t) -(givenFullTime - currenFullTime);
	}
}

/**
 * @invokedBy startup.s
 */
void clock_tick() {
	interrupt_disable();
	if (++ticks == 0) { // 当时间发生溢出时
		++decade;
	}
	task_tick();
	if (task_need_switching()) {
		task_pend_switch();
	}
	interrupt_enable();
}

/**
 * 获取在经过给定刻数之后的周期数。
 * 
 * @param ticks 给定刻数
 * @return 周期数
 */
uint32_t clock_decade_after_ticks(uint32_t ticks) {
	uint32_t currentDecade = clock_decade_get();
	uint32_t currentTime = clock_get();
	if (UINT32_MAX - currentTime < ticks) {
		return currentDecade + 1;
	} else {
		return currentDecade;
	}
}

/**
 * 获取在经过给定刻数后的系统刻数。
 * 
 * @param ticks 给定刻数
 * @return 之后的刻数
 */
uint32_t clock_time_after_ticks(uint32_t ticks) {
	uint32_t currentTime = clock_get();
	return currentTime + ticks;
}
